#include <user/ulib.h>

void
mutex_init(struct mutex_t *mutex)
{
	 sl_init(&mutex->lock);
	 mutex->cur = ENV_NULL;
	 mutex->sleep = ENV_NULL;
}

void
mutex_lock(struct mutex_t *mutex)
{
	 sl_lock(&mutex->lock);

	 /* NOINTR_S; */
	 /* kprintf("[mutex_lock] %d, mutex = %08x, cur = %d, sleep = %d\n", self_idx, va2pa(mutex), mutex->cur, mutex->sleep); */
	 /* NOINTR_E; */
	 
	 if (mutex->cur == ENV_NULL)
	 {
		  mutex->cur = self_idx;
		  
		  sl_release(&mutex->lock);
	 }
	 else
	 {
		  if (mutex->sleep == ENV_NULL)
			   mutex->sleep = self_idx;
		  
		  sleep(mutex->sleep, &mutex->lock);		  
	 }
}

bool
mutex_trylock(struct mutex_t *mutex)
{
	 bool result;
	 
	 sl_lock(&mutex->lock);

	 if (mutex->cur == ENV_NULL)
	 {
		  mutex->cur = self_idx;
		  result = true;
	 }
	 else result = false;
	 
	 sl_release(&mutex->lock);

	 return result;
}

void
mutex_unlock(struct mutex_t *mutex)
{
	 sl_lock(&mutex->lock);

	 /* NOINTR_S; */
	 /* kprintf("[mutex_unlock] %d, mutex = %08x, cur = %d, sleep = %d\n", self_idx, va2pa(mutex), mutex->cur, mutex->sleep); */
	 /* NOINTR_E; */

	 if (mutex->cur == self_idx)
	 {
		  uint16_t next = mutex->sleep;
			   
		  mutex->cur = next;

		  if (next != ENV_NULL)
		  {
			   uint16_t next_sleep = envs[next].sched_info.next;
			   
			   if (next_sleep == next)
					next_sleep = ENV_NULL;
			   mutex->sleep = next_sleep;
			   
			   wakeup(next, &mutex->lock);			   
		  }
		  else sl_release(&mutex->lock);
	 }
	 else
	 {
		  sl_release(&mutex->lock);
	 }
}
